package edu.mcw.rgd.clinminer.umls;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;

import edu.mcw.rgd.clinminer.data.ClinminerException;
import edu.mcw.rgd.clinminer.domain.Study;
import edu.mcw.rgd.clinminer.umls.domain.DataIndex;
import edu.mcw.rgd.clinminer.umls.domain.MrConso;
import edu.mcw.rgd.clinminer.umls.domain.MrDef;
import edu.mcw.rgd.clinminer.umls.domain.MrHier;
import edu.mcw.rgd.clinminer.umls.type.AUI;
import edu.mcw.rgd.clinminer.umls.type.LocalCode;

public class AUITerm {

	MrConso mrconso = null;

	public AUITerm(AUI aui) throws ClinminerException {
		mrconso = MrConso.findMrConso(aui);
		if (mrconso == null) {
			throw new ClinminerException("AUI &lt;" + aui
					+ "&gt; not found in UMLS");
		}
	}

	public AUITerm(LocalCode code) throws ClinminerException {
		mrconso = MrConso.findMrConso(code);
		if (mrconso == null) {
			throw new ClinminerException("Local code &lt;" + code
					+ "&gt; not found in UMLS");
		}
	}

	public AUITerm(MrConso mrconso) {
		this.mrconso = mrconso;
	}

	public String getAccession() {
		return mrconso.getAui();
	}

	public String getLocalCode() {
		return mrconso.getCode();
	}

	private final int RESULTS_LIMIT = 200;

	// TODO: looks highly inefficient where is this used?
	public String getSourceAbbreviation() throws ClinminerException {
		String source = mrconso.getSab();
		// UMLS creates external root terms
		// need to query deeper to get correct underlying source
		if (source.equals("SRC")) {
			List<AUITerm> children = new ArrayList<AUITerm>(getChildren(null));
			if (children.isEmpty()) {
				throw new ClinminerException("No child terms for "
						+ mrconso.getAui());
			}
			AUITerm firstChild = children.get(0);
			return firstChild.getSourceAbbreviation();
		}
		return source;
	}

	public String getLabel() {
		return mrconso.getStr();
	}

	public Collection<AUITerm> getParents(final Study study)
			throws ClinminerException {

		final AUI thisAUI = new AUI(mrconso.getAui());
		Callable<Collection<AUI>> functor = null;
		if (study == null) {
			functor = new Callable<Collection<AUI>>() {
				@Override
				public Collection<AUI> call() throws Exception {
					// this is different
					return MrHier.getParentAUIs(thisAUI, RESULTS_LIMIT);
				}
			};
		} else {
			functor = new Callable<Collection<AUI>>() {
				@Override
				public Collection<AUI> call() throws Exception {
					// this is different
					return DataIndex.getParentAUIs(thisAUI, study);
				}
			};
		}
		return getGenericHierarchyResult(functor);
	}


	public Collection<AUITerm> getChildren(final Study study)
			throws ClinminerException {
		final AUI thisAUI = new AUI(mrconso.getAui());
		Callable<Collection<AUI>> functor = null;
		if (study == null) {
			functor = new Callable<Collection<AUI>>() {
				@Override
				public Collection<AUI> call() throws Exception {
					// this is different
					return MrHier.getChildAUIs(thisAUI, RESULTS_LIMIT);
				}
			};
		} else {
			functor = new Callable<Collection<AUI>>() {
				@Override
				public Collection<AUI> call() throws Exception {
					// this is different
					return DataIndex.getChildAUIs(thisAUI, study);
				}
			};
		}
		return getGenericHierarchyResult(functor);

	}

	public Collection<AUITerm> getSiblings(final Study study)
			throws ClinminerException {
		final AUI thisAUI = new AUI(mrconso.getAui());
		Callable<Collection<AUI>> functor = null;
		if (study == null) {
			functor = new Callable<Collection<AUI>>() {
				@Override
				public Collection<AUI> call() throws Exception {
					// this is different
					return MrHier.getSiblingAUIs(thisAUI, RESULTS_LIMIT);
				}
			};
		} else {
			functor = new Callable<Collection<AUI>>() {
				@Override
				public Collection<AUI> call() throws Exception {
					// this is different
					return DataIndex.getSiblingAUIs(thisAUI, study);
				}
			};
		}
		return getGenericHierarchyResult(functor);

	}

	public Collection<AUITerm> getGenericHierarchyResult(
			Callable<Collection<AUI>> functor) throws ClinminerException {
		Map<String, AUITerm> map = new TreeMap<String, AUITerm>();
		Collection<AUI> AUIs = null;
		try {
			AUIs = functor.call();
		} catch (Exception e) {
			throw new ClinminerException(e);
		}
		for (AUI aui : AUIs) {
			AUITerm term = new AUITerm(aui);
			map.put(term.getLabel(), term);
		}
		return map.values();

	}

	public Boolean hasChildren() {
		return MrHier.hasChildren(mrconso.getAui());
	}

	public Collection<String> getSynonyms() {
		Collection<String> set = mrconso.getSynonyms();
		set.remove(mrconso.getStr().toLowerCase());
		return set;
	}

	public String getCui() {
		return mrconso.getCui();
	}

	public Collection<String> getDefinitions() {
		return MrDef.getDefinitionsForAUI(mrconso.getAui());
	}

	public Integer getAnnotationCount(Study study) throws ClinminerException {
		if (study == null) {
			return DataIndex.getTotalCounts(new AUI(mrconso.getAui()));
		} else {
			return DataIndex.getStudyCounts(new AUI(mrconso.getAui()), study);
		}
	}
}
